﻿namespace USElections.Model
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Json;
    using System.Text;
    using AgFx;
    using USElections.Model.JSON;

    /// <summary>
    /// VotesLoadContext class.
    /// </summary>
    public class VotesLoadContext : LoadContext
    {
        #region Constructor
        /// <summary>
        /// Initializes a new instance of the VotesLoadContext class.
        /// </summary>
        /// <param name="dummy">A dummy object.</param>
        public VotesLoadContext(string dummy)
            : base(dummy)
        {
        }
        #endregion
    }

    /// <summary>
    /// VotesModel class.
    /// </summary>
    [CachePolicy(CachePolicy.CacheThenRefresh, 60 * 60 * 6)] // 6 hours.
    public class VotesModel : ModelItemBase<VotesLoadContext>
    {
        #region Fields
        private States states;
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the VotesModel class.
        /// </summary>
        public VotesModel()
        {
        }

        /// <summary>
        /// Initializes a new instance of the VotesModel class.
        /// </summary>
        /// <param name="dummy">A dummy object.</param>
        public VotesModel(string dummy)
            : base(new VotesLoadContext(dummy))
        {
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or sets the states.
        /// </summary>
        public States States
        {
            get
            {
                return this.states;
            }
            set
            {
                this.states = value;
                RaisePropertyChanged("States");
            }
        }

        /// <summary>
        /// Gets the Total Vote Count.
        /// </summary>
        [DependentOnProperty(PrimaryPropertyName = "States")]
        public int TotalVoteCount
        {
            get
            {
                if (this.States != null)
                {
                    return this.States.TotalNumberOfVotesPerState().Sum(x => x.Value);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the total votes by US citizens.
        /// </summary>
        [DependentOnProperty(PrimaryPropertyName = "States")]
        public int NationalVoteCount
        {
            get
            {
                if (this.States != null)
                {
                    return this.States.TotalNumberOfVotesPerState().Where(x => x.Key.Name != "XX").Sum(x => x.Value);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the total votes by non-US citizens.
        /// </summary>
        [DependentOnProperty(PrimaryPropertyName = "States")]
        public int ForeignVoteCount
        {
            get
            {
                if (this.States != null)
                {
                    return this.States.TotalNumberOfVotesPerState().Where(x => x.Key.Name == "XX").Sum(x => x.Value);
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the Candidate that is leading by Electoral vote.
        /// </summary>
        [DependentOnProperty(PrimaryPropertyName = "States")]
        public USElections.Model.Candidate CandidateLeadingByElectoralVote
        {
            get
            {
                if (this.States != null)
                {
                    var list = this.States.ElectoralVotesPerCandidate();
                    int highestNumberOfVotes = list.Max(x => x.Value);

                    return list.Where(x => x.Value == highestNumberOfVotes).Select(x => x.Key).First();
                }

                return default(USElections.Model.Candidate);
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Serialize JSON string.
        /// </summary>
        /// <typeparam name="T">Object to serialize to.</typeparam>
        /// <param name="json">The input json string.</param>
        /// <returns>The object value.</returns>
        private static T ReadToObject<T>(string json)
        {
            T deserializedUser = default(T);

            try
            {
                MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
                deserializedUser = (T)ser.ReadObject(ms);
                ms.Close();
            }
            catch
            {
            }

            return deserializedUser;
        }
        #endregion

        /// <summary>
        /// VotesDataLoader class.
        /// </summary>
        public class VotesDataLoader : IDataLoader<VotesLoadContext>
        {
            #region Public methods
            public object Deserialize(VotesLoadContext loadContext, Type objectType, System.IO.Stream stream)
            {
                StreamReader reader = new StreamReader(stream);
                string json = reader.ReadToEnd();

                return new VotesModel() { LoadContext = loadContext, States = ReadToObject<States>(json) };
            }

            public LoadRequest GetLoadRequest(VotesLoadContext loadContext, Type objectType)
            {
                string uri = string.Format("{0}?{1}", "http://www.uselectionsapp.com/data/votes.php", DateTime.Now.Ticks);

                return new WebLoadRequest(loadContext, new Uri(uri));
            }
            #endregion
        }
    }
}